package checkers;

import ai.State;
import java.util.ArrayList;
import java.util.List;

public class CheckersState implements State {

    public static final byte BLANK_POSITION = 0;
    public static final byte WHITE_COMMON = 2;
    public static final byte WHITE_KING = 3;
    public static final byte BLACK_COMMON = 8;
    public static final byte BLACK_KING = 9;
    public static final byte WHITE_PLAYER = 1;
    public static final byte BLACK_PLAYER = 2;
    private byte[][] board;
    private byte currentPlayer;
    private byte whiteCommonPiecesAmount;
    private byte whiteKingPiecesAmount;
    private byte blackCommonPiecesAmount;
    private byte blackKingPiecesAmount;

    public CheckersState(byte currentPlayer) {
        this.currentPlayer = currentPlayer;
    }

    public void refreshPiecesAmount() {
        whiteCommonPiecesAmount = 0;
        whiteKingPiecesAmount = 0;
        blackCommonPiecesAmount = 0;
        blackKingPiecesAmount = 0;
        for (int i = 0; i < 8; i++) {
            for (int j = (i % 2 == 0) ? 1 : 0; j < 8; j += 2) {
                switch (this.board[i][j]) {
                    case WHITE_COMMON:
                        whiteCommonPiecesAmount++;
                        break;
                    case WHITE_KING:
                        whiteKingPiecesAmount++;
                        break;
                    case BLACK_COMMON:
                        blackCommonPiecesAmount++;
                        break;
                    case BLACK_KING:
                        blackKingPiecesAmount++;
                        break;
                }
            }
        }
    }

    public byte[][] getBoard() {
        return board;
    }

    public void setBoard(byte[][] board) {
        this.board = board;
    }

    public byte getCurrentPlayer() {
        return currentPlayer;
    }

    @Override
    public CheckersState clone() {
        CheckersState target = new CheckersState(this.currentPlayer);
        byte[][] newBoard = new byte[this.board.length][this.board.length];
        for (short i = 0; i < newBoard.length; i++) {
            for (short j = 0; j < newBoard.length; j++) {
                newBoard[i][j] = this.board[i][j];
            }
        }
        target.board = newBoard;
        target.whiteCommonPiecesAmount = this.whiteCommonPiecesAmount;
        target.whiteKingPiecesAmount = this.whiteKingPiecesAmount;
        target.blackCommonPiecesAmount = this.blackCommonPiecesAmount;
        target.blackKingPiecesAmount = this.blackKingPiecesAmount;
        return target;
    }

    public void applyMovement(CheckersMovement movement) {
        if (movement == null) {
            return;
        }
        byte piece = 0;
        CheckersOperation lastPos = null;
        for (CheckersOperation op : movement.getMovementsList()) {
            switch (op.operation) {
                case CheckersOperation.SOURCE_POSITION:
                    piece = this.board[op.x][op.y];
                    if (lastPos != null) {
                        this.board[lastPos.x][lastPos.y] = CheckersState.BLANK_POSITION;
                    }
                    lastPos = op;
                    break;
                case CheckersOperation.TARGET_POSITION:
                    if (lastPos != null) {
                        this.board[lastPos.x][lastPos.y] = CheckersState.BLANK_POSITION;
                    }
                    lastPos = op;
                    this.board[op.x][op.y] = piece;
                    break;
                case CheckersOperation.REMOVE_PIECE:
                    switch (this.board[op.x][op.y]) {
                        case CheckersState.WHITE_COMMON:
                            whiteCommonPiecesAmount--;
                            break;
                        case CheckersState.WHITE_KING:
                            whiteKingPiecesAmount--;
                            break;
                        case CheckersState.BLACK_COMMON:
                            blackCommonPiecesAmount--;
                            break;
                        case CheckersState.BLACK_KING:
                            blackKingPiecesAmount--;
                            break;
                    }
                    this.board[op.x][op.y] = CheckersState.BLANK_POSITION;
                    break;
                case CheckersOperation.BECOME_KING:
                    switch (this.board[op.x][op.y]) {
                        case CheckersState.WHITE_COMMON:
                            this.board[op.x][op.y] = CheckersState.WHITE_KING;
                            whiteKingPiecesAmount++;
                            whiteCommonPiecesAmount--;
                            break;
                        case CheckersState.BLACK_COMMON:
                            this.board[op.x][op.y] = CheckersState.BLACK_KING;
                            blackKingPiecesAmount++;
                            blackCommonPiecesAmount--;
                            break;
                    }
                    break;
            }
        }
        invertPlayer();

    }

    @Override
    public float getUtilityValue(byte maxPlayer) {
        /*if (whitePiecesAmount == 0) {
         if (maxPlayer == WHITE_PLAYER) {
         return Float.NEGATIVE_INFINITY;
         } else {
         return Float.POSITIVE_INFINITY;
         }
         }

         if (blackPiecesAmount == 0) {
         if (maxPlayer == WHITE_PLAYER) {
         return Float.POSITIVE_INFINITY;
         } else {
         return Float.NEGATIVE_INFINITY;
         }
         }

         if (CheckersProblem.avaiableMovements(this).isEmpty()) {
         if (this.currentPlayer == maxPlayer) {
         return Float.NEGATIVE_INFINITY;
         } else {
         return Float.POSITIVE_INFINITY;
         }
         } else {
         this.invertPlayer();
         if (CheckersProblem.avaiableMovements(this).isEmpty()) {
         this.invertPlayer();
         if (this.currentPlayer == maxPlayer) {
         return Float.NEGATIVE_INFINITY;
         } else {
         return Float.POSITIVE_INFINITY;
         }
         }
         this.invertPlayer();
         }*/
        return 0;
    }

    private void invertPlayer() {
        this.currentPlayer = (this.currentPlayer == WHITE_PLAYER ? BLACK_PLAYER : WHITE_PLAYER);
    }

    @Override
    public List<State> getChildren() {
        List<CheckersMovement> avaiableMovements = CheckersProblem.avaiableMovements(this);
        List<State> list = new ArrayList<State>();
        for (CheckersMovement movement : avaiableMovements) {
            CheckersState clone = this.clone();
            clone.applyMovement(movement);
            list.add(clone);
        }
        return list;
    }

    @Override
    public float heuristicValue(byte maxPlayer) {
        /*int whiteCommonPiecesAmount = 0;
         int whiteKingPiecesAmount = 0;
         //int whiteAvaiableMovements = 0;
         int blackCommonPiecesAmount = 0;
         int blackKingPiecesAmount = 0;
         //int blackAvaiableMovements = 0;

         for (int i = 0; i < 8; i++) {
         for (int j = (i % 2 == 0) ? 1 : 0; j < 8; j += 2) {
         switch (this.board[i][j]) {
         case WHITE_COMMON:
         whiteCommonPiecesAmount++;
         break;
         case WHITE_KING:
         whiteKingPiecesAmount++;
         break;
         case BLACK_COMMON:
         blackCommonPiecesAmount++;
         break;
         case BLACK_KING:
         blackKingPiecesAmount++;
         break;
         }
         }
         }*/

        /*if (this.currentPlayer == WHITE_PLAYER) {
         whiteAvaiableMovements = CheckersProblem.avaiableMovements(this).size();
         this.invertPlayer();
         blackAvaiableMovements = CheckersProblem.avaiableMovements(this).size();
         this.invertPlayer();
         } else {
         blackAvaiableMovements = CheckersProblem.avaiableMovements(this).size();
         this.invertPlayer();
         whiteAvaiableMovements = CheckersProblem.avaiableMovements(this).size();
         this.invertPlayer();
         }*/

        byte inversion = 0;
        switch (maxPlayer) {
            case WHITE_PLAYER:
                inversion = 1;
                break;
            case BLACK_PLAYER:
                inversion = -1;
                break;
        }

        return 3 * (inversion * whiteKingPiecesAmount + inversion * -1 * blackKingPiecesAmount)
                + 2 * (inversion * whiteCommonPiecesAmount + inversion * -1 * blackCommonPiecesAmount);

        /*if (maxPlayer == WHITE_PLAYER) {
         return 3 * (whiteKingPiecesAmount - blackKingPiecesAmount)
         + 2 * (whiteCommonPiecesAmount - blackCommonPiecesAmount);
         // + (whiteAvaiableMovements - blackAvaiableMovements);
         } else {
         return 3 * (blackKingPiecesAmount - whiteKingPiecesAmount)
         + 2 * (blackCommonPiecesAmount - whiteCommonPiecesAmount);
         //  + (blackAvaiableMovements - whiteAvaiableMovements);
         }*/


    }

    @Override
    public byte getPlayer() {
        return this.currentPlayer;
    }
}
